# -*- coding: utf-8 -*-

#This file is part of pyLot library.
#
# pyLot is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# pyLot is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with pyLot.  If not, see <http://www.gnu.org/licenses/>.

__author__ = u'Guillaume Baty'
__copyright__ = u"Copyright 2011-2012 (C) andheo, Université de Pau et des Pays de l'Adour"
__credits__ = [u'Guillaume Baty']
__license__ = "GNU Lesser General Public License"

__all__ = ['ICliModifier', 'ICliSimpleModifier', 'ICli']

from traits.api import Interface


u"""
This module provides classes used to generate console interfaces based on 
argparse module.

Creating ICli* class fills argparse parser (adds console arguments like --command).
Once sys.argv arguments have been parsed with argparse (namespace, argv = parser_app.parse_known_args(argv)),
ICli* handles them using different steps, depending on interface used :
  - ICli
  - ICliSimpleModifier
  - ICliModifier

ICli do simple standalone actions like --print-hello or --verbose.

def parse(self, namespace):
  if namespace.print_hello :
    print "hello"

ICliSimpleModifier do simple actions requiring a specific target.
It does work in one step : parse(namespace, target). These class are generally 
called manually in specific places in program.

ICliModifier is linked to a "target" class and works in 4 steps

  - parseBefore : do all stand alone actions that must be done before target creation
  - parseClass : do all actions linked to a class target
  - parseInstance : do all actions linked to instances of this class
  - parseAfter : do all stand actions that must be done after targets creation

In other words, ICliModifier creates a command line interface and links 
command line commands to (target) class methods.

For example, an option allowing to custom application appearance, sometime 
must be set before widget instance creation because option value is required 
by widget constructor. In this case, you should use parseClass method and
change a static class flag.

For example, --docks to use docks instead of tabs or sub-windows

def parseClass(self, namespace, target=None):
  if namespace.use_docks and target :
    target.DISPLAY_MODE = 'DOCKS' # target == MainWindow class for example
    
Using same example, if appearance can be changed dynamically, you should use
parseInstanceArgs instead to avoid modifying default behaviour.

def parseInstance(self, namespace, target=None):
  if namespace.use_docks and target :
    target.resetDisplayMode(mode='DOCKS') # target == instance of MainWindow


Calls to parse methods is done by main application/main program for example :

[import ...]

parser = argparse.ArgumentParser(version='0.1')
cli = ImplementationCliModifier(parser) # fill parser with --args

[instructions]

cli.parseBefore(namespace)
cli.parseClass(namespace, MyClass)
obj = MyClass()
cli.parseInstance(namespace, obj)
cli.parseAfter(namespace, obj)

[end]

One can imagine to define "parseBefore" actions at the beginning of "parseClass"
and "parseAfter" at the end of "parseInstance" to reduce complexity but it is 
important to separate parseBefore,parseClass and parseInstance,parseAfter
because generally more than one climodifiers are called. A program generally
does :

for cli in climodifiers :
  cli.parseBefore(namespace)
for cli in climodifiers :
  cli.parseClass(namespace, MyClass)
obj = MyClass()
for cli in climodifiers :
  cli.parseInstance(namespace, obj)
for cli in climodifiers :
  cli.parseAfter(namespace, obj)
  
If there are two "treatments" A and B, result is
parseBeforeA
parseBeforeB
parseClassA
parseClassB
...

If parseBefore and parseClass were defined in one method, result would be
parseBeforeA
parseClassA
parseBeforeB
parseClassB
"""

class ICli(Interface):
  def __init__(self, parser):
    u"""
    Fills argparse parser.
    """

  def parse(self, namespace):
    pass

class ICliSimpleModifier(Interface):
  def __init__(self, parser):
    u"""
    Fills argparse parser.
    """

  def parseTarget(self, namespace, target):
    pass


class ICliModifier(Interface):
  u"""
  Class used to generate a console interface based on argparse.
  An ICliModifier is linked to a "target" class or interface.
  
  Creating ICliModifier fills argparse parser (adds console arguments like --command).
  Once sys.argv arguments have been parsed with argparse (namespace, argv = parser_app.parse_known_args(argv)),
  ICliModifier handles them using 3 steps :
  
    - parseBefore : do all stand alone actions that must be done before target creation
    - parseClass : do all actions linked to a specific class
    - parseInstance : do all actions linked to a specific instance of this class
    - parseAfter : do all stand alone actions that must be done after target creation

  In other words, ICliModifier creates a command line interface and links 
  command line commands to (target) class methods.

  For example, an option allowing to custom application appearance, sometime 
  must be set before widget instance creation because option value is required 
  by widget constructor. In this case, you should use parseClassArgs method and
  change a static class flag.
  
  For example, --docks to use docks instead of tabs or sub-windows
  
  def parseClass(self, namespace, target=None):
    if namespace.use_docks and target :
      target.DISPLAY_MODE = 'DOCKS' # target == MainWindow class for example
      
  Using same example, if appearance can be changed dynamically, you should use
  parseInstanceArgs instead to avoid modifying default behaviour.

  def parseInstance(self, namespace, target=None):
    if namespace.use_docks and target :
      target.resetDisplayMode(mode='DOCKS') # target == instance of MainWindow

  Finally, very simple commands (for example --print-hello) does not need a 
  specific class or instance and can be hard coded in ICliModifier.
  In this case, just use "parseAfter" or "parseBefore" or a simple ICli class
  if treatments are totaly independent of target state. :
  
  def parseArgs(self, namespace):
    if namespace.print_hello :
      print "hello"
      
  Calls to parse methods is done by main application/main program for example :

  [import ...]
  
  parser = argparse.ArgumentParser(version='0.1')
  cli = ImplementationCliModifier(parser) # fill parser with --args
  
  [instructions]
 
  cli.parseBefore(namespace)
  cli.parseClass(namespace, MyClass)
  obj = MyClass()
  cli.parseInstance(namespace, obj)
  cli.parseAfter(namespace, obj)
 
  [end]
  
  cli.parseStop(namespace)
 
  On can imagine to define "parseBefore" actions at the beginning of "parseClass"
  and "parseAfter" at the end of "parseInstance" to reduce complexity but it is 
  important to separate parseBefore,parseClass and parseInstance,parseAfter
  because generally more than one climodifiers are called. A program generally
  does :
  
  for cli in climodifiers :
    cli.parseBefore(namespace)
  for cli in climodifiers :
    cli.parseClass(namespace, MyClass)
  obj = MyClass()
  for cli in climodifiers :
    cli.parseInstance(namespace, obj)
  for cli in climodifiers :
    cli.parseAfter(namespace, obj)
    
  If there are two "treatments" A and B, result is
  parseBeforeA
  parseBeforeB
  parseClassA
  parseClassB
  ...

  If parseBefore and parseClass were defined in one method, result would be
  parseBeforeA
  parseClassA
  parseBeforeB
  parseClassB
  """

  def __init__(self, parser):
    u"""
    Fills argparse parser.
    """

  def parseBefore(self, namespace):
    pass

  def parseAfter(self, namespace, target):
    pass

  def parseStop(self, namespace):
    pass

  def parseClass(self, namespace, target):
    u"""
    This method is called before target instantiation.
    All work done here will be available at target instantiation (__init__)
    target is a class.
    
    You should check target's validity.
    """

  def parseInstance(self, namespace, target):
    u"""
    This method is called after target instantiation.
    target is an instance

    You should check target's validity.
    """
